#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

const char* getUserName()
{
    const char* USER = getenv("USER");
    return USER == NULL?"None":USER;
}

const char* getHostName()
{
    const char* HOSTNAME = getenv("HOSTNAME");
    return HOSTNAME == NULL?"None":HOSTNAME;
}

const char* getHome()
{
    const char* HOME = getenv("HOME");
    return HOME == NULL?"None":HOME;
}

std::string getCwd()
{
    char cwdenv[1024];
    char* cwd = getcwd(cwdenv,sizeof(cwdenv));
    
    return cwd == NULL? "None":cwdenv; // 因为返回的是局部变量,所以使用string,进行拷贝
}

const char* getOldPwd()
{
    const char* OLDPWD = getenv("OLDPWD");
    return OLDPWD == NULL?"None":OLDPWD;
}

char pwd[1024];
void cmdPrompt()
{
    std::string path = getCwd();

    // 更新环境变量PWD,不能putenv局部变量。
    snprintf(pwd,sizeof(pwd),"PWD=%s",path.c_str());
    putenv(pwd);

    if(path.size() != 1) // 不是/根目录
    {
        int index = path.rfind('/');
        path = path.substr(index+1);
    }

    std::cout<<"["<<getUserName()<<"@"<<getHostName()<<" "<<path<<"]"<<"$$";
}

bool getCmd(char cmd[],int size)
{
    char* s = fgets(cmd,size,stdin);
    if(s != NULL)
    {
        int len = strlen(cmd);
        cmd[len-1] = '\0'; // fgets会读取'\n',需要处理
    }
    return s != NULL;
}

bool parseCmd(char cmd[],char* argv[])
{
    const char* delim = " ";
    char* token = strtok(cmd,delim);
    int i = 0;
    while(token != NULL)
    {
        argv[i] = token;
        token = strtok(NULL,delim);
        ++i;
    }
    argv[i] = NULL; // 进程替换,要求以NULL结尾

    return i != 0;
}

char oldPwd[1024];
void cd(char* argv[])
{ 
    std::string cwdenv = getCwd();

    if(argv[1] == NULL || strcmp(argv[1],"~") == 0)
    {
        snprintf(oldPwd,sizeof(oldPwd),"OLDPWD=%s",cwdenv.c_str());
        putenv(oldPwd); // 更新OLDPWD

        chdir(getHome());
    }
    else if(strcmp(argv[1],"-") == 0)
    {
        chdir(getOldPwd());
        
        snprintf(oldPwd,sizeof(oldPwd),"OLDPWD=%s",cwdenv.c_str());

        putenv(oldPwd);
    }
    else
    {
        snprintf(oldPwd,sizeof(oldPwd),"OLDPWD=%s",cwdenv.c_str());
        putenv(oldPwd); // 更新OLDPWD
       
        chdir(argv[1]);
    }
}

int lastExitno = 0;
void echo(char* argv[])
{
    if(argv[1] == NULL)
        return;
   
    std::string cmd = argv[1];
    // echo $?
    // echo $PATH
    // echo "hello world"

    if(cmd[0] == '$')
    {
        if(cmd.substr(1) == "?")
        {
            std::cout<<lastExitno<<std::endl;
            lastExitno = 0;
        }
        else
        {
            if(getenv(cmd.substr(1).c_str()))
                std::cout<<getenv(cmd.substr(1).c_str())<<std::endl;
        }
    }
    else
    {
        std::cout<<cmd<<std::endl;
    }
}
bool executeBuiltIn(char* argv[])
{
    std::string cmd = argv[0];
    if(cmd == "cd")
    {
        cd(argv);
        return true;
    }
    else if(cmd == "echo")
    {
        echo(argv);
        return true;
    }
    // ...
    else
    {

    }

    return false;
}

int executeExternal(char* argv[])
{
    pid_t id = fork();
    if(id == -1)
        return 1;
    if(id == 0)
    {
        execvp(argv[0],argv);
        exit(2);
    }
    int status = 0;

    pid_t wid = waitpid(id,&status,0);

    if(wid == id && WIFEXITED(status)) // 子进程退出,且是正常退出
        lastExitno = WEXITSTATUS(status);

    return 0;
}

int main()
{
    while(true)
    {
        // 命令提示符
        cmdPrompt();

        // 获取用户输入命令
        char cmd[1024] = {0};
        if(!getCmd(cmd,sizeof(cmd)))
            continue;

        // 解析命令
        char* argv[1024] = {0};
        if(!parseCmd(cmd,argv))
            continue;

        // 内建命令
        if(executeBuiltIn(argv))
            continue;

        // 执行命令
        executeExternal(argv);
    }

    return 0;
}
